#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

using Weazel.Math;
using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;

namespace Weazel.Gezel.Parser
{
    /// <summary>
    /// expression_list:  expression_list semicol_expr | semicol_expr               
    /// </summary>
    public static class ParseExpressionList
    {
        public static readonly Set<Token> First = ParseSemicolonExpression.First;

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            List<Expression> result = new List<Expression>();

            Set<Token> current = ParseSemicolonExpression.First;

            bool done = false;

            do
            {
                t.Check(follow.Union(current));

                if (ParseSemicolonExpression.First.Contains(t.CurrentToken))
                {
                    Expression e = ParseSemicolonExpression.Parse(follow.Union(current), t);

                    if (e is ListExpression)
                    {
                        ListExpression le = e as ListExpression;
                        result.AddRange(le.List);
                    }
                    else
                    {
                        if (e != null)
                            result.Add(e);
                    }
                }
                else
                    done = true;

            } while (!done);

            if (result.Count == 1)
                return result[0];
            else
                return new ListExpression(result.ToArray());
        }
    }

    /// <summary>
    /// semicol_expr: directive SEMICOLON | expr SEMICOLON   {};
    /// </summary>
    public static class ParseSemicolonExpression
    {
        public static readonly Set<Token> First =
          ParseExpression.First.Union(ParseDirective.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            Set<Token> current = new Set<Token>(Token.Semicolon);

            // bracnh

            t.Check(follow.Union(current).Union(First));

            if (ParseExpression.First.Contains(t.CurrentToken))
            {
                Expression e = ParseExpression.Parse(follow.Union(Token.Semicolon), t);
                t.Accept(Token.Semicolon, follow);
                return e;
            }
            else if (ParseDirective.First.Contains(t.CurrentToken))
            {
                Expression e = ParseDirective.Parse(follow.Union(Token.Semicolon), t);
                t.Accept(Token.Semicolon, follow);
                return e;
            }
            else
            {
                t.Errors.Add(
                  new UnexpectedTokenParseError(
                    t.CurrentPosition,
                    t.CurrentToken,
                    follow.Union(current)
                  )
                );

                return null;
            }
        }
    }

    /// <summary>
    /// expr:   assignment_expr 
    ///       | expr COMMA assignment_expr
    /// </summary>
    public class ParseExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseAssignmentExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseExpression'",
              ConsoleColor.Red
            );

            List<Expression> expressions = new List<Expression>();

            Set<Token> current = new Set<Token>(
              Token.Comma
            );

            current = current.Union(ParseAssignmentExpression.First);

            bool done = false;

            do
            {
                Expression e = ParseAssignmentExpression.Parse(follow.Union(current), t);

                if (e != null)
                    expressions.Add(e);

                t.Check(follow.Union(current));

                if (t.CurrentToken == Token.Comma)
                {
                    t.Accept(Token.Comma, follow.Union(current));
                }
                else
                {
                    done = true;
                }

            } while (!done);

            if (expressions.Count == 1)
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseExpression'",
                  ConsoleColor.Yellow
                );

                return expressions[0];
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseExpression'",
                  ConsoleColor.Yellow
                );

                return new ListExpression(expressions.ToArray());
            }
        }
    }

    /// <summary>
    /// assignment_expr: conditional_expr 
    ///                | unary_expr assignment_op assignment_expr
    ///  ;
    /// </summary>
    public class ParseAssignmentExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseConditionalExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseAssignmentExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(Token.Assign);
            current = current.Union(ParseConditionalExpression.First);

            Expression e = ParseConditionalExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Assign)
            {
                current.Remove(Token.Assign);
                t.Accept(Token.Assign, follow.Union(current));

                // An assignment expression cannot have an arbitary lefthand side..

                // this error is caught in the verification phase
                //
                //        if(e.ExpressionType != ExpressionType.Identifier)
                //          t.Errors.Add(
                //            new ParseError(
                //              ParseErrorType.InvalidAssignmentLeftExpression,
                //              new ErrorMessage(
                //                "Only identifer allowed on left side of assignment"
                //              )
                //            )
                //          );

                ParserDebug.WriteLine(
                  "Leaving 'ParseAssignmentExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Assignment,
                  e,
                  ParseAssignmentExpression.Parse(follow, t),
                  startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseAssignmentExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    /// <summary>
    /// conditional_expr:   ior_expr 
    ///                   | ior_expr QMARK expr COLON conditional_expr 
    ///              ;
    /// </summary>
    public class ParseConditionalExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseIorExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseConditionalExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Quest,
              Token.Colon
            );

            Expression condition = ParseIorExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Quest)
            {
                current.Remove(Token.Quest);
                t.Accept(Token.Quest, follow.Union(current));

                Expression left = ParseExpression.Parse(follow.Union(Token.Colon).Union(First), t);

                t.Accept(Token.Colon, follow);

                ParserDebug.WriteLine(
                  "Leaving 'ParseConditionalExpression'",
                  ConsoleColor.Yellow
                );

                return new TernaryExpression(
                  condition,
                  left,
                  ParseConditionalExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseConditionalExpression'",
                  ConsoleColor.Yellow
                );

                return condition;
            }
        }
    }

    /// <summary>
    /// ior_expr: xor_expr | ior_expr IOR xor_expr 
    /// </summary>
    public class ParseIorExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseXorExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseIorExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Ior
            );

            current = current.Union(ParseXorExpression.First);

            Expression e = ParseXorExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Ior)
            {
                current.Remove(Token.Ior);
                t.Accept(Token.Ior, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseIorExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Ior,
                  e,
                  ParseIorExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseIorExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    public class ParseXorExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseAndExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseXorExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Xor
            );

            current = current.Union(ParseAndExpression.First);

            Expression e = ParseAndExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Xor)
            {
                current.Remove(Token.Xor);
                t.Accept(Token.Xor, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseXorExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Xor,
                  e,
                  ParseXorExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseXorExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    public class ParseAndExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseEqualsExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseAndExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.And
            );

            current = current.Union(ParseEqualsExpression.First);

            Expression e = ParseEqualsExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.And)
            {
                current.Remove(Token.And);
                t.Accept(Token.And, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseAndExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.And,
                  e,
                  ParseAndExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseAndExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }


    /// <summary>
    /// equal_expr: shift_expr
    ///      | equal_expr EQ shift_expr 
    ///      | equal_expr NE shift_expr 
    ///      | equal_expr SMT shift_expr 
    ///      | equal_expr GRT shift_expr 
    ///      | equal_expr SMTEQ shift_expr 
    ///      | equal_expr GRTEQ shift_expr 
    ///			;
    /// </summary>
    public class ParseEqualsExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseShiftExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseEqualsExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Eq,
              Token.Neq,
              Token.Lt,
              Token.Gt,
              Token.Lte,
              Token.Gte
            );

            current = current.Union(ParseShiftExpression.First);

            Expression e = ParseShiftExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            current.Remove(
              Token.Eq,
              Token.Neq,
              Token.Lt,
              Token.Gt,
              Token.Lte,
              Token.Gte
            );

            if (t.CurrentToken == Token.Eq)
            {
                t.Accept(Token.Eq, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Eq,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Neq)
            {
                t.Accept(Token.Neq, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Neq,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Lt)
            {
                t.Accept(Token.Lt, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Lt,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Gt)
            {
                t.Accept(Token.Gt, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Gt,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Lte)
            {
                t.Accept(Token.Lte, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Lte,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Gte)
            {
                t.Accept(Token.Gte, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Gte,
                  e,
                  ParseEqualsExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseEqualsExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    public class ParseShiftExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseAddExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseShiftExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Sla,
              Token.Sra
            );

            Expression e = ParseAddExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Sla)
            {
                t.Accept(Token.Sla, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseShiftExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.ShiftLeft,
                  e,
                  ParseShiftExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Sra)
            {
                t.Accept(Token.Sra, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseShiftExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.ShiftRight,
                  e,
                  ParseShiftExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseShiftExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    public class ParseAddExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseMulExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseAddExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Add,
              Token.Sub,
              Token.Concat
            );

            Expression e = ParseMulExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Add)
            {
                t.Accept(Token.Add, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseAddExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Add,
                  e,
                  ParseAddExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Sub)
            {
                t.Accept(Token.Sub, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseAddExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Sub,
                  e,
                  ParseAddExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Concat)
            {
                t.Accept(Token.Concat, follow.Union(First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseAddExpression'",
                  ConsoleColor.Yellow
                );

                // Note: Here the children of the binary expression
                // are swapped (in comparsion with the two other operators 
                // of this rule). This is necessary inorder to do a left-
                // to-right convatenation
                return new BinaryExpression(
                  ExpressionType.Concatenation,
                  ParseAddExpression.Parse(follow, t),
                  e,
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseAddExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    /// <summary>
    /// mul_expr: cast_expr 
    ///      | mul_expr MUL cast_expr 
    ///      | mul_expr MOD cast_expr 
    ///      ;
    /// </summary>
    public class ParseMulExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParseCastExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseMulExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Mul,
              Token.Mod,
              Token.Div
            );

            current = current.Union(ParseMulExpression.First);

            Expression e = ParseCastExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentStartPosition;

            t.Check(follow.Union(current));

            current.Remove(Token.Mul, Token.Mod);

            if (t.CurrentToken == Token.Mul)
            {
                t.Accept(Token.Mul, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseMulExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Mul,
                  e,
                  ParseMulExpression.Parse(follow, t),
                  startPosition,
                            t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Div)
            {
                t.Accept(Token.Div, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseMulExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Div,
                  e,
                  ParseMulExpression.Parse(follow, t),
                  startPosition,
                  t.CurrentEndPosition
                );
            }
            else if (t.CurrentToken == Token.Mod)
            {
                t.Accept(Token.Mod, follow.Union(current));

                ParserDebug.WriteLine(
                  "Leaving 'ParseMulExpression'",
                  ConsoleColor.Yellow
                );

                return new BinaryExpression(
                  ExpressionType.Mod,
                  e,
                  ParseMulExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseMulExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    /// <summary>
    /// cast_expr: unary_expr 
    ///      | RBRACKET_OPEN sig_type RBRACKET_CLOSE cast_expr 
    /// ;
    /// </summary>
    public class ParseCastExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>(Token.LeftParenthesis).Union(ParseUnaryExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseCastExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.LeftParenthesis,
              Token.RightParenthesis
            );

            current = current.Union(ParseSignalType.First);
            current = current.Union(ParseUnaryExpression.First);
            current = current.Union(ParseCastExpression.First); // redundant

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            current.Remove(Token.LeftParenthesis);

            if (t.CurrentToken == Token.LeftParenthesis)
            {
                current.Remove(Token.LeftParenthesis);

                t.Accept(Token.LeftParenthesis, follow.Union(current));

                t.Check(follow.Union(current));

                if (ParseSignalType.First.Contains(t.CurrentToken))
                {
                    /*
                    current.Remove(Token.LeftParenthesis);
                    t.Accept(Token.LeftParenthesis, follow.Union(current));
                    */

                    current.Remove(ParseSignalType.First);
                    SignalTypeInfo type = ParseSignalType.Parse(follow.Union(current), t);

                    current.Remove(Token.RightParenthesis);
                    t.Accept(Token.RightParenthesis, follow.Union(current));

                    current.Remove(ParseCastExpression.First);
                    Expression e = ParseCastExpression.Parse(follow, t);

                    ParserDebug.WriteLine(
                      "Leaving 'ParseCastExpression'",
                      ConsoleColor.Yellow
                    );

                    return new CastExpression(
                        type.Width,
                        type.Signed,
                        e, startPosition,
                        t.CurrentEndPosition
                    );
                }
                else
                {
                    Expression e = ParseExpression.Parse(follow.Union(Token.RightParenthesis), t);
                    t.Accept(Token.RightParenthesis, follow);

                    ParserDebug.WriteLine(
                      "Leaving 'ParseCastExpression'",
                      ConsoleColor.Yellow
                    );

                    return e;
                }
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseCastExpression'",
                  ConsoleColor.Yellow
                );

                return ParseUnaryExpression.Parse(follow, t);
            }
        }
    }

    /// <summary>
    /// unary_expr: postfix_expr  /* pre-increment and pre-decrement inplace should be added here */
    ///      | SUB cast_expr  
    ///      | NOT cast_expr  
    ///      ;
    /// </summary>
    public class ParseUnaryExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>(Token.Sub, Token.Not).Union(ParsePostFixExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseUnaryExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Sub,
              Token.Add
            );

            current = current.Union(ParsePostFixExpression.First);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Sub)
            {
                t.Accept(Token.Sub, follow.Union(ParseCastExpression.First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseUnaryExpression'",
                  ConsoleColor.Yellow
                );

                Expression e = ParseCastExpression.Parse(follow, t);

                // Fold '-' and constants .. to unsigned 
                if (e.ExpressionType == ExpressionType.Constant)
                {
                    string value = ((LeafExpression)e).Value;

                    System.Diagnostics.Debug.Assert(value.Length > 0 && Char.IsDigit(value[value.Length - 1]));

                    return new LeafExpression(
                      ExpressionType.Constant,
                      "-" + value,
                                  startPosition,
                                  t.CurrentEndPosition
                    );
                }
                else
                {
                    return new UnaryExpression(
                      ExpressionType.UnarySub,
                      e,
                                  startPosition,
                                  t.CurrentEndPosition
                    );
                }
            }
            else if (t.CurrentToken == Token.Not)
            {
                t.Accept(Token.Not, follow.Union(ParseCastExpression.First));

                ParserDebug.WriteLine(
                  "Leaving 'ParseUnaryExpression'",
                  ConsoleColor.Yellow
                );

                return new UnaryExpression(
                  ExpressionType.Not,
                  ParseCastExpression.Parse(follow, t),
                            startPosition,
                            t.CurrentEndPosition
                );
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParseUnaryExpression'",
                  ConsoleColor.Yellow
                );

                return ParsePostFixExpression.Parse(follow, t);
            }
        }
    }

    /// <summary>
    /// postfix_expr: primary_expr
    ///         | postfix_expr BRACKET_OPEN rangeexp BRACKET_CLOSE 
    ///         | postfix_expr RBRACKET_OPEN conditional_expr RBRACKET_CLOSE 
    ///         ;
    /// </summary>
    public class ParsePostFixExpression
    {
        public static readonly Set<Token> First =
          new Set<Token>().Union(ParsePrimaryExpression.First);

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParsePostFixExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.LeftBracket,
              Token.LeftParenthesis,
              Token.RightBracket,
              Token.RightParenthesis
            );

            current = current.Union(ParseConditionalExpression.First);
            current = current.Union(ParseRangeExpression.First);

            Expression e = ParsePrimaryExpression.Parse(follow.Union(current), t);

            Position startPosition = t.CurrentPosition;

            t.Check(follow.Union(current));

            current.Remove(Token.LeftBracket, Token.LeftParenthesis);

            if (t.CurrentToken == Token.LeftBracket)
            {
                t.Accept(Token.LeftBracket, follow.Union(current));

                int to = 0, from = 0;
                ParseRangeExpression.Parse(follow.Union(Token.RightBracket), t, ref from, ref to);

                t.Accept(Token.RightBracket, follow);

                ParserDebug.WriteLine(
                  "Leaving 'ParsePostFixExpression'",
                  ConsoleColor.Yellow
                );

                return new RangeExpression((uint)from, (uint)to, e, startPosition, t.CurrentEndPosition);
            }
            else if (t.CurrentToken == Token.LeftParenthesis)
            {
                current.Remove(ParseRangeExpression.First);
                current.Remove(Token.LeftBracket, Token.RightBracket);

                t.Accept(Token.LeftParenthesis, follow.Union(current));

                current.Remove(ParseConditionalExpression.First);
                Expression indexExpression =
                  ParseConditionalExpression.Parse(follow.Union(current), t);

                t.Accept(Token.RightParenthesis, follow);

                return new IndexExpression(e, indexExpression, startPosition, t.CurrentEndPosition);
            }
            else
            {
                ParserDebug.WriteLine(
                  "Leaving 'ParsePostFixExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
        }
    }

    public class ParseRangeExpression
    {
        public static readonly Set<Token> First = new Set<Token>(
          Token.Number
        );

        public static void Parse(Set<Token> follow, Tokenizer t, ref int from, ref int to)
        {
            ParserDebug.WriteLine(
              "Entering 'ParseRangeExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Number,
              Token.Colon
            );

            if (t.CurrentToken == Token.Number)
            {
                to = t.CurrentInt;

                // also set from (will be overridden if needed)
                from = t.CurrentInt;
            }

            t.Accept(Token.Number, follow.Union(current));

            // parse 'optional' parts

            t.Check(follow.Union(current));

            if (t.CurrentToken == Token.Colon)
            {
                current.Remove(Token.Colon);
                t.Accept(Token.Colon, follow.Union(current));

                if (t.CurrentToken == Token.Number)
                    from = t.CurrentInt;

                t.Accept(Token.Number, follow);
            }

            ParserDebug.WriteLine(
              "Leaving 'ParseRangeExpression'",
              ConsoleColor.Yellow
            );
        }
    }

    public class ParsePrimaryExpression
    {
        public static readonly Set<Token> First = new Set<Token>(
          Token.Identifier,
          Token.Number,
          Token.LeftParenthesis
        );

        public static Expression Parse(Set<Token> follow, Tokenizer t)
        {
            ParserDebug.WriteLine(
              "Entering 'ParsePrimaryExpression'",
              ConsoleColor.Red
            );

            Set<Token> current = new Set<Token>(
              Token.Identifier,
              Token.Number,
              Token.LeftParenthesis,
              Token.RightParenthesis
            );

            t.Check(follow.Union(current));

            Position startPosition = t.CurrentPosition;
            System.Diagnostics.Debug.Assert(startPosition != null);

            if (t.CurrentToken == Token.Identifier)
            {
                string value = t.CurrentString;
                t.Accept(Token.Identifier, follow);

                ParserDebug.WriteLine(
                  "Leaving 'ParsePrimaryExpression'",
                  ConsoleColor.Yellow
                );

                return new LeafExpression(ExpressionType.Identifier, value, startPosition, t.CurrentEndPosition);
            }
            else if (t.CurrentToken == Token.Number)
            {
                string value = t.CurrentString;
                t.Accept(Token.Number, follow);

                ParserDebug.WriteLine(
                  "Leaving 'ParsePrimaryExpression'",
                  ConsoleColor.Yellow
                );

                return new LeafExpression(ExpressionType.Constant, value, startPosition, t.CurrentEndPosition);
            }
            else if (t.CurrentToken == Token.LeftParenthesis)
            {
                current.Remove(Token.Identifier, Token.Number, Token.LeftParenthesis);
                t.Accept(Token.LeftParenthesis, follow.Union(current));

                Expression e = ParseExpression.Parse(follow.Union(current), t);

                t.Accept(Token.RightParenthesis, follow);

                ParserDebug.WriteLine(
                  "Leaving 'ParsePrimaryExpression'",
                  ConsoleColor.Yellow
                );

                return e;
            }
            else
            {
                t.Errors.Add(
                  new UnexpectedTokenParseError(
                    t.CurrentPosition,
                    t.CurrentToken,
                    follow.Union(current)
                  )
                );

                ParserDebug.WriteLine(
                  "Leaving 'ParsePrimaryExpression'",
                  ConsoleColor.Yellow
                );

                return new LeafExpression(ExpressionType.Constant, "0");
            }
        }
    }
}